from ext import *
import os
import sys
import xml.sax as sax

class DepedencieException(Exception):
    pass

class MetaInfHandler(sax.handler.ContentHandler):

    def __init__(self): 
        self.dependencies = []
        self.active = None
        self.version = ''
        self.homepage = ''

    def startElement(self, name, attrs): 
        if name == 'version': 
            self.active = name
        elif name == 'component':
            self.dependencies.append(('component', str(attrs['name'])))
        elif name == 'bundle': 
            self.dependencies.append(('bundle', str(attrs['name'])))
        elif name == 'homepage':
            self.homepage = str(attrs['url'])

    def endElement(self, name): 
        pass

    def characters(self, content): 
        if self.active == 'version':
            self.version = str(content.strip())
            self.active = None

class ConsoleLogger(object):

    def log(self, data):
        print(data)
        
class LibLoader(object):

    def __init__(self):
        self.components = {}
        
    def register_component(self, name, obj):
        self.components[name] = obj
        
    def has_component(self, name):
        return name in self.components.keys()
        
    def get_component(self, name):
        return self.components[name]
        

class BundleManager(object):
    '''
    This class loads the bundles an starts and stops them.
    '''

    __inst = None

    @staticmethod
    def get_instance():
        if not BundleManager.__inst:
            BundleManager.__inst = BundleManager()

        return BundleManager.__inst

    def add_path(self, path):
        path = os.path.abspath(path)
        self.paths.append(path)
        sys.path.append(path)
        folders = (folder for folder in os.listdir(path) if os.path.isdir(os.path.abspath(os.path.join(path, folder))))
        for folder in folders:
            if os.path.exists(os.path.abspath(os.path.join(path, folder, '__init__.py'))) and \
                os.path.exists(os.path.abspath(os.path.join(path, folder, 'metainf.xml'))):
                self._bundles[folder] = __import__(folder)
                handler = MetaInfHandler() 
                parser = sax.make_parser() 
                parser.setContentHandler(handler) 
                parser.parse(os.path.join(path, folder, 'metainf.xml'))
                self.dependencies[folder] = handler.dependencies
                self.homepages[folder] = handler.homepage
                if not hasattr(self._bundles[folder], 'start'):
                    self.logger.log(interpolate('invalid bundle #folder, start method is missing, fix it by adding one'))
                    del self._bundles[folder]
                elif not hasattr(self._bundles[folder], 'stop'):
                    self.logger.log(interpolate('invalid bundle #folder, stop method is missing, fix it by adding one'))
                    del self._bundles[folder]

    #@private
    def __init__(self):
        self._bundles = {}
        self.logger = ConsoleLogger()
        self.active_bundles = {}
        self.paths = []
        self.loader = LibLoader()
        self.dependencies = {}
        self.homepages = {}

    def getDependencies(self, name):
        return self.dependencies[name]

    def activateBundle(self, name, params={}):
        params['loader'] = self.loader
        params['bundle_manager'] = self
        if name in self.active_bundles.keys():
            return
        for type, _name in self.dependencies[name]:
            if type == 'bundle':
                self.activateBundle(_name, params)
                
        for type, _name in self.dependencies[name]:
            if type == 'component':
                if not self.loader.has_component(_name):
                    na = _name
                    raise DepedencieException(interpolate('lib #na not found'))
        try:
            self._bundles[name].start(params)
            self.active_bundles[name] = self._bundles[name]
        except Exception:
            e = sys.exc_info()[1]
            self.logger.log(interpolate('error activating bundle #name : ' + str(e)))

    def stopBundle(self, name):
        for key, value in self.dependencies.items():
            for type, _name in value:
                if type == 'bundle' and name == _name:
                    raise DepedencieException('cannot stop bundle {0} because bundle {1} depdends on it'.format(name, key))
        try:
            self._bundles[name].stop()
            del self.active_bundles[name]
        except Exception:
            e = sys.exc_info()[1]
            self.logger.log(interpolate('error stopping bundle #name : ' + str(e)))

    def isBundleActive(self, name):
        if name in self._bundles.keys():
            return True
        else:
            return False

    def activateAllBundles(self, params):
        for name in self._bundles.keys():
            self.activateBundle(name, params)

    def deactivateAllBundles(self):
        for name in self._bundles.keys():
            try:
                self._bundles[name].stop()
                del self.active_bundles[name]
            except Exception:
                e = sys.exc_info()[1]
                self.logger.log(interpolate('error activating bundle #name : #e'))

    @property
    def bundles(self):
        return self._bundles
